package cn.turboinfo.dongying.api.domain.admin.usecase.role;

import cn.turboinfo.dongying.api.domain.admin.service.menu.MenuService;
import cn.turboinfo.dongying.api.domain.common.service.role.RolePermissionService;
import cn.turboinfo.dongying.api.domain.common.service.role.RoleService;
import cn.turboinfo.dongying.api.domain.common.usecase.AbstractUseCase;
import cn.turboinfo.dongying.api.entity.admin.fo.menu.CheckableMenuFO;
import cn.turboinfo.dongying.api.entity.admin.pojo.menu.Menu;
import cn.turboinfo.dongying.api.entity.admin.pojo.role.Role;
import cn.turboinfo.dongying.api.entity.admin.pojo.role.RolePermission;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 列表角色可被分配的权限
 *
 * @author sunshow
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class AdminListRoleCheckablePermissionUseCase extends AbstractUseCase<AdminListRoleCheckablePermissionUseCase.InputData, AdminListRoleCheckablePermissionUseCase.OutputData> {

    private final RoleService roleService;

    private final RolePermissionService rolePermissionService;

    private final MenuService menuService;

    @Override
    protected OutputData doAction(InputData inputData) {
        Long roleId = inputData.getRoleId();

        Role role = roleService.getByIdEnsure(roleId);

        // 指定角色已经有的权限ID
        Set<Long> permissionIdSet = rolePermissionService.findByRoleId(role.getId()).stream().map(RolePermission::getPermissionId).collect(Collectors.toSet());

        Set<Long> checkedIdSet = new HashSet<>();

        // 半选中的节点, 即不是所有子节点都是选中状态
        Set<Long> halfCheckedIdSet = new HashSet<>();

        List<CheckableMenuFO> checkableMenuFOList = menuService.findRoleAccessibleWithHierarchy(roleId).stream().map(menu -> {
            CheckableMenuFO fo = new CheckableMenuFO();
            fo.setId(menu.getId());
            fo.setName(menu.getName());
            fo.setDescription(menu.getDescription());
            fo.setParentId(menu.getParentId());
            fo.setResource(menu.getResource());
            fo.setUrl(menu.getUrl());
            fo.setSubList(convertCheckableMenu(permissionIdSet, menu, checkedIdSet, halfCheckedIdSet));

            if (permissionIdSet.contains(menu.getId())) {
                if (fo.getSubList() == null || fo.getSubList().isEmpty()) {
                    // 叶子节点自己选中就是选中
                    fo.setChecked(true);
                } else {
                    // 遍历子节点看是否都选中
                    boolean checked = true;
                    boolean halfChecked = false;
                    for (CheckableMenuFO subFO : fo.getSubList()) {
                        if (!subFO.isChecked()) {
                            // 只要有一个没选中就肯定不是选中
                            checked = false;
                        }
                        if (subFO.isHalfChecked()) {
                            // 只要有一个是半选中的父级就是半选中
                            halfChecked = true;
                        }
                    }
                    fo.setChecked(checked);
                    fo.setHalfChecked(halfChecked);
                }
            }

            if (fo.isChecked()) {
                checkedIdSet.add(fo.getId());
            }
            if (fo.isHalfChecked()) {
                halfCheckedIdSet.add(fo.getId());
            }

            return fo;
        }).collect(Collectors.toList());

        Map<String, Object> map = new HashMap<>();
        map.put("role", role);
        map.put("checkableList", checkableMenuFOList);
        map.put("checkedIdList", checkedIdSet);
        map.put("halfCheckedIdList", halfCheckedIdSet);

        return OutputData.builder()
                .role(role)
                .checkableList(checkableMenuFOList)
                .checkedIdList(new ArrayList<>(checkedIdSet))
                .halfCheckedIdList(new ArrayList<>(halfCheckedIdSet))
                .build();
    }

    private List<CheckableMenuFO> convertCheckableMenu(Set<Long> permissionIdSet, Menu menu, Set<Long> checkedIdSet, Set<Long> halfCheckedIdSet) {
        List<CheckableMenuFO> subList = new ArrayList<>();
        if (menu.getSubList() == null) {
            return subList;
        }
        for (Menu sub : menu.getSubList()) {
            CheckableMenuFO fo = new CheckableMenuFO();
            fo.setId(sub.getId());
            fo.setName(sub.getName());
            fo.setDescription(sub.getDescription());
            fo.setParentId(sub.getParentId());
            fo.setResource(sub.getResource());
            fo.setUrl(sub.getUrl());
            fo.setSubList(convertCheckableMenu(permissionIdSet, sub, checkedIdSet, halfCheckedIdSet));

            if (permissionIdSet.contains(sub.getId())) {
                if (fo.getSubList() == null || fo.getSubList().isEmpty()) {
                    // 叶子节点自己选中就是选中
                    fo.setChecked(true);
                } else {
                    // 遍历子节点看是否都选中
                    boolean checked = true;
                    boolean halfChecked = false;
                    for (CheckableMenuFO subFO : fo.getSubList()) {
                        if (!subFO.isChecked()) {
                            // 只要有一个没选中就肯定不是选中
                            checked = false;
                        }
                        if (subFO.isHalfChecked()) {
                            // 只要有一个是半选中的父级就是半选中
                            halfChecked = true;
                        }
                    }
                    fo.setChecked(checked);
                    fo.setHalfChecked(halfChecked);
                }
            }

            if (fo.isChecked()) {
                checkedIdSet.add(fo.getId());
            }
            if (fo.isHalfChecked()) {
                halfCheckedIdSet.add(fo.getId());
            }

            subList.add(fo);
        }
        return subList;
    }

    @Getter
    @Setter
    @Builder
    public static class InputData extends AbstractUseCase.InputData {

        @NotNull(message = "用户不能为空")
        private Long roleId;

    }

    @Getter
    @Setter
    @Builder
    public static class OutputData extends AbstractUseCase.OutputData {

        private Role role;

        private List<CheckableMenuFO> checkableList;

        private List<Long> checkedIdList;

        private List<Long> halfCheckedIdList;

    }
}
